// Decompiled on 周六 2月 22 19:31:49 CST 2025 with Zomboid Decompiler v0.1.3 using Vineflower.
package zombie.ui;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import se.krka.kahlua.integration.LuaReturn;
import se.krka.kahlua.luaj.compiler.LuaCompiler;
import se.krka.kahlua.stdlib.BaseLib;
import se.krka.kahlua.vm.KahluaException;
import se.krka.kahlua.vm.LuaClosure;
import zombie.Lua.LuaManager;
import zombie.characters.IsoGameCharacter;
import zombie.core.Core;
import zombie.debug.DebugOptions;

public final class UIDebugConsole extends NewWindow {
    public static UIDebugConsole instance;
    IsoGameCharacter ParentChar;
    ScrollBar ScrollBarV;
    UITextBox2 OutputLog;
    public UITextBox2 CommandLine;
    UITextBox2 autosuggest;
    String ConsoleVersion = "v1.1.0";
    int inputlength = 0;
    private final ArrayList<String> Previous = new ArrayList();
    private final ArrayList<Method> globalLuaMethods = new ArrayList();
    public int PreviousIndex = 0;
    Method prevSuggestion = null;
    String[] AvailableCommands = new String[]{"?", "help", "commands", "clr", "AddInvItem", "SpawnZombie"};
    String[] AvailableCommandsHelp = new String[]{
        "'?' - Shows available commands",
        "'help' - Shows available commands",
        "'commands' - Shows available commands",
        "'clr' - Clears the command log",
        "'AddInvItem' - Adds an item to player inventory. USAGE - AddInvItem 'ItemName' [ammount]",
        "'SpawnZombie' - Spawn a zombie at a map location. USAGE - SpawnZombie X,Y,Z (integers)"
    };
    public boolean bDebounceUp = false;
    public boolean bDebounceDown = false;
    private static final Object outputLock = "DebugConsole Output Lock";
    private static final ByteBuffer outputBB = ByteBuffer.allocate(8192);
    private static boolean outputChanged = false;
    private static CharsetDecoder outputDecoder;
    private static char[] outputChars;
    private static CharBuffer outputCharBuf = null;

    public UIDebugConsole(int x, int y) {
        super(x, y, 10, 10, true);
        this.ResizeToFitY = false;
        this.visible = true;
        instance = this;
        this.width = 640.0F;
        int _int = TextManager.instance.getFontHeight(UIFont.DebugConsole);
        byte byte0 = 11;
        byte byte1 = 5;
        this.OutputLog = new UITextBox2(
            UIFont.DebugConsole,
            5,
            33,
            630,
            _int * byte0 + byte1 * 2,
            "Project Zomboid - "
                + Core.getInstance().getVersion()
                + "\nDebug Console - "
                + this.ConsoleVersion
                + "\n(C) Indie Stone Studios 2021\n---------------------------------------------------------------------------------------------------------------------------\n\n",
            true
        );
        this.OutputLog.multipleLine = true;
        this.OutputLog.bAlwaysPaginate = false;
        this.CommandLine = new UIDebugConsole.CommandEntry(
            UIFont.DebugConsole, 5, (int)(this.OutputLog.getY() + this.OutputLog.getHeight()) + 15, 630, 24, "", true
        );
        this.CommandLine.IsEditable = true;
        this.CommandLine.TextEntryMaxLength = 256;
        this.autosuggest = new UITextBox2(UIFont.DebugConsole, 5, 180, 15, 25, "", true);
        this.height = (float)((int)(this.CommandLine.getY() + this.CommandLine.getHeight()) + 6);
        this.ScrollBarV = new ScrollBar(
            "UIDebugConsoleScrollbar",
            null,
            (int)(this.OutputLog.getX() + this.OutputLog.getWidth()) - 14,
            this.OutputLog.getY().intValue() + 4,
            this.OutputLog.getHeight().intValue() - 8,
            true
        );
        this.ScrollBarV.SetParentTextBox(this.OutputLog);
        this.AddChild(this.OutputLog);
        this.AddChild(this.ScrollBarV);
        this.AddChild(this.CommandLine);
        this.AddChild(this.autosuggest);
        this.InitSuggestionEngine();
        if (Core.bDebug) {
            BaseLib.setPrintCallback(this::SpoolText);
        }
    }

    @Override
    public void render() {
        if (this.isVisible()) {
            super.render();
            this.DrawTextCentre(UIFont.DebugConsole, "Command Console", this.getWidth() / 2.0, 2.0, 1.0, 1.0, 1.0, 1.0);
            this.DrawText(UIFont.DebugConsole, "Output Log", 7.0, 19.0, 0.7F, 0.7F, 1.0, 1.0);
            this.DrawText(UIFont.DebugConsole, "Lua Command Line", 7.0, this.OutputLog.getY() + this.OutputLog.getHeight() + 1.0, 0.7F, 0.7F, 1.0, 1.0);
        }
    }

    @Override
    public void update() {
        if (this.isVisible()) {
            this.handleOutput();
            super.update();
            if (this.CommandLine.getText().length() != this.inputlength && this.CommandLine.getText().length() != 0) {
                this.inputlength = this.CommandLine.getText().length();
                String[] string0 = this.CommandLine.getText().split(":");
                String string1 = "";
                if (string0.length > 0) {
                    string1 = string0[string0.length - 1];
                    if (string0[string0.length - 1].isEmpty() && this.autosuggest.isVisible()) {
                        this.autosuggest.setVisible(false);
                        return;
                    }
                }

                Method method0 = null;
                if (string0.length > 1 && string0[0].indexOf(")") > 0 && !string0[string0.length - 1].contains("(")) {
                    ArrayList arrayList = new ArrayList(this.globalLuaMethods);

                    for (int _int = 0; _int < string0.length; _int++) {
                        String string2 = string0[_int];
                        if (string2.indexOf(")") > 0) {
                            string2 = string2.split("\\(", 0)[0];

                            for (Method method1 : arrayList) {
                                if (method1.getName().equals(string2)) {
                                    arrayList.clear();

                                    for (Class _class = method1.getReturnType(); _class != null; _class = _class.getSuperclass()) {
                                        for (Method method2 : _class.getDeclaredMethods()) {
                                            if (Modifier.isPublic(method2.getModifiers())) {
                                                arrayList.add(method2);
                                            }
                                        }
                                    }
                                    break;
                                }
                            }
                        }
                    }

                    method0 = this.SuggestionEngine(string1, arrayList);
                } else if (string0.length == 1) {
                    method0 = this.SuggestionEngine(string1);
                }

                String string3 = "void";
                if (method0 != null) {
                    if (!method0.getReturnType().toString().equals("void")) {
                        String[] string4 = method0.getReturnType().toString().split("\\.");
                        string3 = string4[string4.length - 1];
                    }

                    if (!this.autosuggest.isVisible()) {
                        this.autosuggest.setVisible(true);
                    }

                    this.autosuggest.SetText("<" + string3 + "> " + method0.getName());
                    this.autosuggest.setX((double)(5 * this.CommandLine.getText().length()));
                    this.autosuggest.setWidth((double)(15 * (string3.length() + method0.getName().length())));
                    this.autosuggest.Frame.width = (float)(10 * (string3.length() + method0.getName().length()));
                }
            } else if (this.CommandLine.getText().length() == 0 && this.autosuggest.isVisible()) {
                this.autosuggest.setVisible(false);
            }
        }
    }

    public void ProcessCommand() {
        if (this.CommandLine.internalText != null) {
            String string0 = this.CommandLine.internalText;
            this.CommandLine.internalText = "";
            string0 = string0.trim();
            String[] string1 = string0.split(" ");
            string1[0] = string1[0].trim();
            if (this.Previous.isEmpty() || !string0.equals(this.Previous.get(this.Previous.size() - 1))) {
                this.Previous.add(string0);
            }

            this.PreviousIndex = this.Previous.size();
            this.CommandLine.DoingTextEntry = true;
            Core.CurrentTextEntryBox = this.CommandLine;
            if ("clear".equals(string0)) {
                this.OutputLog.bTextChanged = true;
                this.OutputLog.clearInput();
            } else {
                if (DebugOptions.instance.UIDebugConsoleEchoCommand.getValue()) {
                    this.SpoolText("[USER] - \"" + string0 + "\".");
                }

                try {
                    LuaClosure luaClosure = LuaCompiler.loadstring(string0, "console", LuaManager.env);
                    LuaReturn luaReturn = LuaManager.caller.protectedCall(LuaManager.thread, luaClosure, new Object[0]);
                } catch (KahluaException kahluaException) {
                    this.SpoolText(kahluaException.getMessage());
                } catch (Exception exception) {
                    Logger.getLogger(UIDebugConsole.class.getName()).log(Level.SEVERE, null, exception);
                }
            }
        }
    }

    void historyPrev() {
        this.PreviousIndex--;
        if (this.PreviousIndex < 0) {
            this.PreviousIndex = 0;
        }

        if (this.PreviousIndex >= 0 && this.PreviousIndex < this.Previous.size()) {
            this.CommandLine.SetText((String)this.Previous.get(this.PreviousIndex));
        }
    }

    void historyNext() {
        this.PreviousIndex++;
        if (this.PreviousIndex >= this.Previous.size()) {
            this.PreviousIndex = this.Previous.size() - 1;
        }

        if (this.PreviousIndex >= 0 && this.PreviousIndex < this.Previous.size()) {
            this.CommandLine.SetText((String)this.Previous.get(this.PreviousIndex));
        }
    }

    public void onOtherKey(int key) {
        switch (key) {
            case 15:
                if (this.prevSuggestion != null) {
                    String[] string = this.CommandLine.getText().split(":");
                    StringBuilder stringBuilder = new StringBuilder();
                    if (string.length > 0) {
                        string[string.length - 1] = this.prevSuggestion.getName();

                        for (int _int = 0; _int < string.length; _int++) {
                            stringBuilder.append(string[_int]);
                            if (_int != string.length - 1) {
                                stringBuilder.append(":");
                            }
                        }
                    }

                    if (this.prevSuggestion.getParameterTypes().length == 0) {
                        this.CommandLine.SetText(stringBuilder + "()");
                    } else {
                        this.CommandLine.SetText(stringBuilder + "(");
                    }
                }
        }
    }

    void ClearConsole() {
        this.OutputLog.bTextChanged = true;
        this.OutputLog.SetText("");
        this.UpdateViewPos();
    }

    void UpdateViewPos() {
        this.OutputLog.TopLineIndex = this.OutputLog.Lines.size() - this.OutputLog.NumVisibleLines;
        if (this.OutputLog.TopLineIndex < 0) {
            this.OutputLog.TopLineIndex = 0;
        }

        this.ScrollBarV.scrollToBottom();
    }

    void SpoolText(String string) {
        this.OutputLog.bTextChanged = true;
        this.OutputLog.SetText(this.OutputLog.Text + string + "\n");
        this.UpdateViewPos();
    }

    Method SuggestionEngine(String string) {
        return this.SuggestionEngine(string, this.globalLuaMethods);
    }

    Method SuggestionEngine(String string, ArrayList<Method> arrayList) {
        int int0 = 0;
        int int1 = 0;
        Method method0 = null;

        for (Method method1 : arrayList) {
            if (method0 == null) {
                method0 = method1;
                int0 = this.levenshteinDistance(string, method1.getName());
            } else {
                int1 = this.levenshteinDistance(string, method1.getName());
                if (int1 < int0) {
                    int0 = int1;
                    method0 = method1;
                }
            }
        }

        this.prevSuggestion = method0;
        return method0;
    }

    void InitSuggestionEngine() {
        Class<LuaManager.GlobalObject> _class = LuaManager.GlobalObject.class;
        this.globalLuaMethods.addAll(Arrays.asList(_class.getDeclaredMethods()));
    }

    public int levenshteinDistance(CharSequence lhs, CharSequence rhs) {
        int int0 = lhs.length() + 1;
        int int1 = rhs.length() + 1;
        int[] int2 = new int[int0];
        int[] int3 = new int[int0];
        int int4 = 0;

        while (int4 < int0) {
            int2[int4] = int4++;
        }

        for (int int5 = 1; int5 < int1; int5++) {
            int3[0] = int5;

            for (int int6 = 1; int6 < int0; int6++) {
                int int7 = lhs.charAt(int6 - 1) == rhs.charAt(int5 - 1) ? 0 : 1;
                int int8 = int2[int6 - 1] + int7;
                int int9 = int2[int6] + 1;
                int int10 = int3[int6 - 1] + 1;
                int3[int6] = Math.min(Math.min(int9, int10), int8);
            }

            int[] int11 = int2;
            int2 = int3;
            int3 = int11;
        }

        return int2[int0 - 1];
    }

    void setSuggestWidth(int _int) {
        this.autosuggest.setWidth((double)_int);
        this.autosuggest.Frame.width = (float)_int;
    }

    public void addOutput(byte[] _byte, int int2, int int0) {
        if (int0 >= 1) {
            synchronized (outputLock) {
                int int1 = int0 - outputBB.capacity();
                if (int1 > 0) {
                    int2 += int1;
                    int0 -= int1;
                }

                if (outputBB.position() + int0 > outputBB.capacity()) {
                    outputBB.clear();
                }

                outputBB.put(_byte, int2, int0);
                if (_byte[int2 + int0 - 1] == 10) {
                    outputChanged = true;
                }
            }
        }
    }

    private void handleOutput() {
        synchronized (outputLock) {
            if (outputChanged) {
                outputChanged = false;

                try {
                    if (outputDecoder == null) {
                        outputDecoder = Charset.forName("UTF-8")
                            .newDecoder()
                            .onMalformedInput(CodingErrorAction.REPLACE)
                            .onUnmappableCharacter(CodingErrorAction.REPLACE);
                    }

                    outputDecoder.reset();
                    int int0 = outputBB.position();
                    outputBB.flip();
                    int int1 = (int)((double)int0 * (double)outputDecoder.maxCharsPerByte());
                    if (outputChars == null || outputChars.length < int1) {
                        int int2 = (int1 + 128 - 1) / 128 * 128;
                        outputChars = new char[int2];
                        outputCharBuf = CharBuffer.wrap(outputChars);
                    }

                    outputCharBuf.clear();
                    CoderResult coderResult = outputDecoder.decode(outputBB, outputCharBuf, true);
                    outputBB.clear();
                    String string = new String(outputChars, 0, outputCharBuf.position());
                    this.OutputLog.bTextChanged = true;
                    this.OutputLog.SetText(this.OutputLog.Text + string);
                    short _short = 8192;
                    if (this.OutputLog.Text.length() > _short) {
                        int int3 = this.OutputLog.Text.length() - _short;

                        while (int3 < this.OutputLog.Text.length() && this.OutputLog.Text.charAt(int3) != '\n') {
                            int3++;
                        }

                        this.OutputLog.bTextChanged = true;
                        this.OutputLog.SetText(this.OutputLog.Text.substring(int3 + 1));
                    }
                } catch (Exception exception) {
                }

                this.UpdateViewPos();
            }
        }
    }

    private class CommandEntry extends UITextBox2 {
        public CommandEntry(UIFont uIFont, int int0, int int1, int int2, int int3, String string, boolean _boolean) {
            super(uIFont, int0, int1, int2, int3, string, _boolean);
        }

        @Override
        public void onPressUp() {
            UIDebugConsole.this.historyPrev();
        }

        @Override
        public void onPressDown() {
            UIDebugConsole.this.historyNext();
        }

        @Override
        public void onOtherKey(int _int) {
            UIDebugConsole.this.onOtherKey(_int);
        }
    }
}
